----------Language Carnival 1----------
A 4am crack                  2016-10-23
---------------------------------------

Name: Language Carnival 1
Genre: educational
Year: 1988
Publisher: Developmental Learning
  Materials
Platform: Apple ][+ or later (64K)
Media: single-sided 5.25-inch floppy
OS: ProDOS 1.4
Previous cracks: none

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  no errors, but copy boots ProDOS,
  displays BASIC prompt, then exits to
  BASIC with no OS in memory

Locksmith Fast Disk Backup
  ditto

EDD 4 bit copy (no sync, no count)
  ditto

Copy ][+ nibble editor
  nothing suspicious

Disk Fixer
  T00 -> standard ProDOS bootloader and
    disk catalog

Why didn't any of my copies work?
  probably a nibble check in the
  startup program

Next steps:

  1. Trace the startup program
  2. Disable the nibble check
  3. Declare victory(*)

(*) go to the gym

                   ~

               Chapter 1
In Which Things Go Better Than Expected


[S6,D1=non-working copy]
[S7,D1=my ProDOS hard drive /A4AMCRACK]

]PR#7
...
]CAT,S6,D1

/CARNIVAL1

 NAME           TYPE  BLOCKS  MODIFIED

*BASIC.SYSTEM    SYS      21  15-NOV-83
*CS              BIN       5  22-SEP-87
 DARTS           BAS      13   5-NOV-87
 GALLERY         BAS      16   5-NOV-87
 MM.WORDS        TXT       9  15-OCT-87
 PRODOS          SYS      32  17-APR-87
 PSET.0B         BIN       6  15-OCT-87
 PSET.0D         BIN       9  15-OCT-87
 PSET.CC         BIN       9  15-OCT-87
 PSET.DT         BIN       6  15-OCT-87
 RR.WORDS        TXT       5  15-OCT-87
 SP.WORDS        TXT       6  15-OCT-87
 START.2         BAS      15   2-NOV-87
 STARTUP         BAS       3  29-OCT-87
 STRENGTH        BAS      16   5-NOV-87
 THROW           BAS      15   5-NOV-87
 LS              BIN      17  15-OCT-87
 SETUP           BIN       1   2-DEC-87
 SETUP2          BIN       1   2-DEC-87

BLOCKS FREE:   68     BLOCKS USED:  212

]PREFIX /CARNIVAL1
]LOAD STARTUP
]LIST

 1  POKE 214,255: POKE 1012,0
 4  PRINT  CHR$ (4);"BLOAD SETUP"

 5  PRINT  CHR$ (4);"BLOAD SETUP2
     ": CALL 12288
 11  POKE 1012,0
 15  LOMEM: 24576
 20 D$ =  CHR$ (4)
 50  HOME : TEXT
 55  PRINT : PRINT D$;"BLOAD CS,A
     $2000": POKE 232,0: POKE 233
     ,32
 60  PRINT D$;"BLOADLS,A$4000"
 65  POKE 230,64:AA =  PEEK (4923
     2) +  PEEK (49239) +  PEEK (
     49237) +  PEEK (49234)
BREAK
]

]BLOAD SETUP           ; loads at $3000
]BLOAD SETUP2          ; loads at $2000
]CALL -151

12288 is $3000, so let's start there.

                   ~

               Chapter 2
 In Which It All Comes Down To One Bit


*3000L

; a simple XOR decryption loop
3000-   A2 E2       LDX   #$E2
3002-   BD 00 20    LDA   $2000,X
3005-   49 FF       EOR   #$FF
3007-   9D 00 20    STA   $2000,X
300A-   CA          DEX
300B-   D0 F5       BNE   $3002

; call the decrypted code
300D-   20 00 20    JSR   $2000

; branch if carry is clear
3010-   90 04       BCC   $3016

; jump to BASIC cold start (never
; returns)
3012-   20 B0 FE    JSR   $FEB0
3015-   60          RTS

; successful execution continues here
; (from $3010) -- set some zero page
; value and exit
3016-   A9 02       LDA   #$02
3018-   85 FE       STA   $FE
301A-   60          RTS

Well it's pretty clear that the code at
$2000 is important (it's encrypted,
after all), and that it is vitally
important that it clear the carry flag
on exit.

; stop after the decryption loop
*300D:60

; decrypt the mysterious code at $2000
*3000G

*2000L

; hard-code slot 6
2000-   A9 60       LDA   #$60
2002-   8D D9 20    STA   $20D9
2005-   A9 05       LDA   #$05
2007-   8D DA 20    STA   $20DA
200A-   AE D9 20    LDX   $20D9

; turn on slot 6 drive motor manually
200D-   BD 8E C0    LDA   $C08E,X
2010-   BD 89 C0    LDA   $C089,X
2013-   A9 00       LDA   #$00
2015-   8D DB 20    STA   $20DB

; wait for drive to spin up
2018-   A0 00       LDY   #$00
201A-   C8          INY
201B-   D0 FD       BNE   $201A
201D-   EE DB 20    INC   $20DB
2020-   D0 F6       BNE   $2018
2022-   A9 00       LDA   #$00
2024-   8C DB 20    STY   $20DB

; get a nibble from disk (not shown)
2027-   20 CD 20    JSR   $20CD
202A-   C8          INY
202B-   D0 08       BNE   $2035
202D-   EE DB 20    INC   $20DB
2030-   D0 03       BNE   $2035
2032-   4C C8 20    JMP   $20C8

; match "D5 AA BB" nibble sequence
2035-   C9 D5       CMP   #$D5
2037-   D0 EE       BNE   $2027
2039-   20 CD 20    JSR   $20CD
203C-   C9 AA       CMP   #$AA
203E-   D0 F5       BNE   $2035
2040-   20 CD 20    JSR   $20CD
2043-   C9 BB       CMP   #$BB
2045-   D0 EE       BNE   $2035

; get some 4-4-encoded values and
; store them locally
2047-   A0 00       LDY   #$00
2049-   20 CD 20    JSR   $20CD
204C-   38          SEC
204D-   2A          ROL
204E-   8D DB 20    STA   $20DB
2051-   20 CD 20    JSR   $20CD
2054-   2D DB 20    AND   $20DB
2057-   99 DC 20    STA   $20DC,Y
205A-   C8          INY
205B-   C0 02       CPY   #$02
205D-   D0 EA       BNE   $2049
205F-   A0 00       LDY   #$00
2061-   20 CD 20    JSR   $20CD
2064-   C8          INY
2065-   C0 04       CPY   #$04
2067-   D0 F8       BNE   $2061

; skip self-sync nibble (#$FF)
2069-   BD 8C C0    LDA   $C08C,X
206C-   10 FB       BPL   $2069
206E-   C9 FF       CMP   #$FF
2070-   D0 4E       BNE   $20C0

; reset data latch
2072-   BD 8D C0    LDA   $C08D,X

; burn some CPU cycles (now we're out
; of phase with the "proper" start of
; nibbles)
2075-   A0 10       LDY   #$10
2077-   A5 09       LDA   $09

; find #$EE in the "out of phase"
; nibbles
2079-   BD 8C C0    LDA   $C08C,X
207C-   10 FB       BPL   $2079
207E-   88          DEY
207F-   F0 3F       BEQ   $20C0
2081-   C9 EE       CMP   #$EE
2083-   D0 F4       BNE   $2079

; store the next 4 "out of phase"
; nibbles locally
2085-   A0 00       LDY   #$00
2087-   BD 8C C0    LDA   $C08C,X
208A-   10 FB       BPL   $2087
208C-   99 DE 20    STA   $20DE,Y
208F-   C8          INY
2090-   C0 04       CPY   #$04
2092-   D0 F3       BNE   $2087

; check the 4-4-encoded values we read
; earlier
2094-   AD DC 20    LDA   $20DC
2097-   CD D3 20    CMP   $20D3
209A-   D0 24       BNE   $20C0
209C-   AD DD 20    LDA   $20DD
209F-   CD D4 20    CMP   $20D4
20A2-   D0 1C       BNE   $20C0

; verify the "out of phase" nibbles
; against an encrypted array
20A4-   A0 00       LDY   #$00
20A6-   B9 DE 20    LDA   $20DE,Y
20A9-   49 87       EOR   #$87
20AB-   38          SEC
20AC-   E9 01       SBC   #$01
20AE-   D9 D5 20    CMP   $20D5,Y
20B1-   D0 0D       BNE   $20C0
20B3-   99 DE 20    STA   $20DE,Y
20B6-   C8          INY
20B7-   C0 04       CPY   #$04
20B9-   D0 EB       BNE   $20A6

; turn off drive motor
20BB-   BD 88 C0    LDA   $C088,X

; clear carry and return to caller
20BE-   18          CLC
20BF-   60          RTS

; failures end up here (from $20B1,
; $20A2, $209A, $207F, or $2070) --
; decrement death counter and try again
; but eventually give up
20C0-   CE DA 20    DEC   $20DA
20C3-   F0 03       BEQ   $20C8
20C5-   4C 22 20    JMP   $2022

; ultimate failure path is here -- turn
; off drive motor, set carry, and
; return to caller
20C8-   BD 88 C0    LDA   $C088,X
20CB-   38          SEC
20CC-   60          RTS

Despite appearances, there are no long-
term side effects here. We're reading
"out of phase" nibbles (like an E7
bitstream check, but different values)
and storing them, then comparing them,
but we don't actually *need* them in
the long run. The caller simply checks
the carry flag and sets a zero page
value (at $3018). The ultimate caller,
the BASIC startup program, immediately
loads another file at $2000 ("CS", on
line 55), which will wipe out all of
this code and the nibble values we
stored.

So it's safe to simply change the code
at $3000 to ignore the encrypted copy
protection code at $2000 and branch to
the success path at $3016.

Turning to my Disk Fixer sector editor,
I can set the DOS type to "PRODOS" and
press "D" to get a directory listing,
then follow the "SETUP" file and find
the $3000 calling code on track $1A.

T1A,S04,$0E: 00 -> BE

(Change the JSR $2000 to JSR $20BE,
which unconditionally clears the carry,
then let the rest of the code think the
check succeeded.)

]PR#6
...works...

Quod erat liberandum.

---------------------------------------
A 4am crack                     No. 886
------------------EOF------------------
